package top.xunfong.redis.expressionEvaluator;

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;

import java.lang.reflect.Method;


public class KeyExpressionEvaluator {
    // SPEL表达式解析器
    private final ExpressionParser parser = new SpelExpressionParser();
    // 参数名发现器，用于获取方法参数名
    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    /**
     * 解析并计算key的SPEL表达式
     *
     * @param method        目标方法
     * @param args          方法参数
     * @param keyExpression SPEL表达式字符串
     * @return 表达式计算结果
     */
    public Object evaluate(Method method, Object[] args, String keyExpression) {
        if (keyExpression == null || keyExpression.isEmpty()) {
            throw new IllegalArgumentException("key表达式不能为空");
        }
        // 1. 创建表达式上下文
        StandardEvaluationContext context = createEvaluationContext(method, args);
        // 2. 解析表达式字符串为Expression对象
        Expression expression = parser.parseExpression(keyExpression);
        // 3. 计算表达式值
        return expression.getValue(context);
    }


    /**
     * 创建SPEL表达式计算上下文
     * 注册方法参数，支持通过#参数名或#p0形式访问
     */
    private StandardEvaluationContext createEvaluationContext(Method method, Object[] args) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        // 注册方法参数名，支持#参数名访问
        String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
        if (parameterNames != null && parameterNames.length > 0) {
            for (int i = 0; i < parameterNames.length; i++) {
                context.setVariable(parameterNames[i], args[i]);
            }
        }
        // 支持通过#args访问参数数组
        context.setVariable("args", args);
        // 支持通过#p0, #p1...访问参数（p0表示第一个参数）
        for (int i = 0; i < args.length; i++) {
            context.setVariable("p" + i, args[i]);
        }
        return context;
    }
}
